package com.df.ez_game;


import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
import com.df.ez_game.server.*;
import com.df.ez_game.server.logic.ISessionLifecycle;
import com.df.ez_game.server.logic.LogicServerNode;
import com.df.ez_game.server.msg.client.ClientReqMsg;
import com.df.ez_game.server.msg.client.ClientRespMsg;
import com.df.ez_game.server.req_metadata.EzMethodData;
import io.netty.channel.Channel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//请求上下文，应该包含：
//1.请求和返回信息
//2.会话信息
//3.服务信息
public class EZGameCtx extends HashMap<String, Object> {
    //TODO 使用fastThreadLocal
    private static ThreadLocal<EZGameCtx> gameCtx = new ThreadLocal<EZGameCtx>();
    public static AttributeKey<Long> loginId = AttributeKey.newInstance("loginId");
    private Thread msgHandleThread;
    private Map<String,Object> attrs = new HashMap<>();
    @Setter
    @Getter
    private ClientReqMsg reqMsg;

    /**
     * 请求的参数对象，是解析方法签名里的类型对象
     */
    @Setter
    @Getter
    private Object reqParam;
    @Setter
    @Getter
    private Channel channel;


    /**
     * 响应给客户端的对象
     */
    @Setter
    @Getter
    private ClientRespMsg respMsg;

    /**
     * 方法的返回对象，方法签名里返回的对象
     */
    @Setter
    @Getter
    private Object respObj;

    /**
     * 是否手动回复，为true的话，方法结束，即使有返回值，也不会发送给前端，要用户自己给前端发送消息
     */
    @Getter
    @Setter
    private boolean manualResp = false;

    private long userId;

    @Setter
    @Getter
    private ServerNode serverNode;
    @Setter
    @Getter
    private UserSession session;

    @Getter
    @Setter
    private EzMethodData methodData;

    /**
     * 在参数解析之前
     */
    @Getter
    @Setter
    private Long reqStartTime;
    /**
     * 在返回值编码之后
     */
    @Getter
    @Setter
    private Long reqEndTime;
    @Getter
    @Setter
    private String status="ok";//fail manualResp

    @Getter
    @Setter
    private String reason;//fail 时才有

    public static EZGameCtx getContext() {
        return gameCtx.get();
    }
    public static void setGameCtx(EZGameCtx context) {
        gameCtx.set(context);
    }
    public static void removeContext() {
        EZGameCtx context = gameCtx.get();
        if(context != null) {
            context.channel = null;
            context.reqParam = null;
            context.respMsg = null;
            context.serverNode = null;
            context.clear();
        }
        gameCtx.remove();
    }



    public boolean channelIsActive() {
        if(channel == null){
            return false;
        }
        if(!channel.isOpen()){
            return false;
        }
        if(!channel.isActive()){
            return false;
        }

        return true;
    }

    //指定用户发
    public void sendMsg(Integer userId,Object msg){
        //根据 userId找到channel
    }
    //指定用户发
    public void sendSelfMsg(Object msg){
        //根据 userId找到channel
    }
    //指定用户广播
    public void broadcast(List<Long> userIds, Integer cmd, Object msg){
        //根据 userId找到channel
        if(serverNode.getType()== NodeType.SingletonLogic){
            LogicServerNode sn = (LogicServerNode) serverNode;
            PushMsgMeta pushMsgMeta = sn.getPushMsgMap().get(cmd);
            if(pushMsgMeta==null){
                throw new RuntimeException("推送cmd:"+cmd+" 不存在元数据，无法推送此消息");
            }
            if(!pushMsgMeta.getPushMsgClass().equals(msg.getClass())){
                throw new RuntimeException("推送cmd:"+cmd+" 需要的类型为["+pushMsgMeta.getPushMsgClass().getSimpleName()+"]，当前发送的为["+msg.getClass().getSimpleName()+"]");
            }
            for (Long id : userIds) {
                UserSession otherSession = sn.getLoginSessions().get(id);
                if(otherSession==null){
                    return;
                }
                ClientRespMsg broad = new ClientRespMsg();
                broad.setCmd(cmd);
                Codec codec = ProtobufProxy.create(msg.getClass());
                try {
                    byte[] encode = codec.encode(msg);
                    broad.setData(encode);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                otherSession.getChannel().writeAndFlush(msg);
            }

            return;
        }
    }
    //全局广播
    public void broadcastAll(String path, Object msg){
        //根据 userId找到channel
    }

    //登录
    public void login(Long id) {
        //单体服务器，直接绑定到channel,否则给中心服发登录消息
        if(serverNode.getType()== NodeType.SingletonLogic){
            session.setUserId(id);
            channel.attr(loginId).set(id);
            LogicServerNode sn = (LogicServerNode) serverNode;
            sn.getLoginSessions().put(id,session);
            ISessionLifecycle sessionLifecycle = sn.getSettings().getSessionLifecycle();
            if(sessionLifecycle!=null){
                sessionLifecycle.onLogin(channel,session,id);
            }
            return;
        }
        //TODO 给中心服发消息
    }
    //登出
    public void logout() {
        //单体服务器，直接绑定到channel,否则给中心服发登录消息
        if(serverNode.getType()== NodeType.SingletonLogic){
            LogicServerNode sn = (LogicServerNode) serverNode;
            Attribute<Long> attr = channel.attr(loginId);
            if(attr.get()!=null){
                sn.getLoginSessions().remove(attr.get());
                ISessionLifecycle sessionLifecycle = sn.getSettings().getSessionLifecycle();
                if(sessionLifecycle!=null){
                    sessionLifecycle.onLoginOut(channel,session);
                }
            }
            return;
        }
        //TODO 给中心服发消息
    }
    //获取登录绑定得id
    public Long getLoginId() {
        //单体服务器，直接绑定到channel,否则给中心服发登录消息
        if(serverNode.getType()== NodeType.SingletonLogic){
            Long s = channel.attr(loginId).get();

            return s;
        }
        //从请求里拿
        return null;
    }

    public <T> void setAttr(EzAttribute<T> playerAttr,T value) {
        attrs.put(playerAttr.getKey(),value );
    }
    public <T> T getAttr(EzAttribute<T> playerAttr) {
        Object o = attrs.get(playerAttr.getKey());
        return (T)o;
    }

    public <T> T getRpc(Class<T> orderRpcClass) {
        return null;

    }

    public void pushSelf(Integer cmd, Object pushMsg) {
        //根据 userId找到channel
        if(serverNode.getType()== NodeType.SingletonLogic){
            LogicServerNode sn = (LogicServerNode) serverNode;
            PushMsgMeta pushMsgMeta = sn.getPushMsgMap().get(cmd);
            if(pushMsgMeta==null){
                throw new RuntimeException("推送cmd:"+cmd+" 不存在元数据，无法推送此消息");
            }
            if(!pushMsgMeta.getPushMsgClass().equals(pushMsg.getClass())){
                throw new RuntimeException("推送cmd:"+cmd+" 需要的类型为["+pushMsgMeta.getPushMsgClass().getSimpleName()+"]，当前发送的为["+pushMsg.getClass().getSimpleName()+"]");
            }
            ClientRespMsg push = new ClientRespMsg();
            push.setCmd(cmd);
            Codec codec = ProtobufProxy.create(pushMsg.getClass());
            try {
                byte[] encode = codec.encode(pushMsg);
                push.setData(encode);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            channel.writeAndFlush(push);

            return;
        }
    }
}
